﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Caching;

using Yahoo.Yui.Compressor;

using CssAndJsCombiner.Configuration;

namespace CssAndJsCombiner
{
    public class ClientResourcesHandler : IHttpHandler
    {
        private static readonly SimpleTextParser textParser = new SimpleTextParser();

        private ClientResourceType _type;

        private bool _canCache = true;
        private bool CanCache
        {
            get { return !WebContext.IsDebugMode && _canCache; }
            set { if (_canCache) _canCache = value; }
        }

        public bool IsReusable
        {
            get { return false; }
        }

        static ClientResourcesHandler()
        {
            string vPath = HttpRuntime.AppDomainAppVirtualPath;
            textParser.Variables.Add("~/", vPath.EndsWith("/") ? vPath : vPath + "/");
        }

        public void ProcessRequest(HttpContext ctx)
        {
            ClientResourcesSection section = ClientResourcesSection.GetSection();
            string pathInfo = ctx.Request.PathInfo.Remove(0, 1);

            _type = (ClientResourceType)Enum.Parse(typeof(ClientResourceType), ctx.Request.FilePath.Split('/').Last().Split('.')[0], true);

            string cache_key = String.Format("clientResource_{0}_{1}", _type, pathInfo);
            string content = ctx.Cache[cache_key] as string;

            if (content == null)
            {
                StringBuilder sb = new StringBuilder();

                if (pathInfo.Contains(".css") || pathInfo.Contains(".js"))
                {
                    load(new ClientResourcePathLoader(), pathInfo, new string[] { }, sb, true);
                }
                else
                {
                    string[] split = ctx.Request.PathInfo.Remove(0, 1).ToLowerInvariant().Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
                    string groupName = split[0];

                    IList<ClientResourceItemElement> items = null;
                    switch (_type)
                    {
                        case ClientResourceType.Css: items = section.Stylesheets.Items[groupName]; break;
                        case ClientResourceType.Js: items = section.Javascripts.Items[groupName]; break;
                    }

                    if (items.Count > 0)
                    {
                        IClientResourceLoader loader = null;
                        string[] vars = split.Skip(1).Take(split.Length - 1).ToArray();

                        foreach (ClientResourceItemElement item in items)
                        {
                            string path = item.Path;
                            path = path.Replace("$(host)", ctx.Request.Url.Host);

                            Type t = Type.GetType(item.Loader);
                            loader = (IClientResourceLoader)Activator.CreateInstance(t);
                         
                            load(loader, path, vars, sb, item.Compress);
                        }
                    }
                }

                content = sb.ToString();
                if (CanCache) ctx.Cache.Add(cache_key, content, null, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
            }

            ctx.Response.ContentType = getContentType(_type);
            ctx.Response.Write(content);

            if (CanCache) WebContext.Current.SetCache(content, 30);
            WebContext.Current.Compress();
        }

        private string getContentType(ClientResourceType type)
        {
            switch (type)
            {
                case ClientResourceType.Css: return "text/css";
                case ClientResourceType.Js: return "text/javascript";
            }

            return "text/html";
        }

        private void load(IClientResourceLoader loader, string path, string[] vars, StringBuilder sb, bool compress)
        {
            string body;

            if (loader.TryLoad(_type, path, vars, out body))
            {
                appendBody(sb, body, path, compress);
            }
            else
            {
                CanCache = false;
            }
        }

        private void appendBody(StringBuilder sb, string body, string path, bool compress)
        {
            body = textParser.ParseContent(body);
            bool debug = WebContext.IsDebugMode;

            if (!debug && compress && !String.IsNullOrEmpty(body))
            {
                switch (_type)
                {
                    case ClientResourceType.Js:
                        JavaScriptCompressor compressor = new JavaScriptCompressor(body);
                        body = compressor.Compress();

                        break;

                    case ClientResourceType.Css:
                        body = CssCompressor.Compress(body, 0, CssCompressionType.Hybrid);

                        break;
                }
            }

            if (debug)
            {
                string fileFormat = String.Empty;
                switch (_type)
                {
                    case ClientResourceType.Css: fileFormat = "/* file: {0} */"; break;
                    case ClientResourceType.Js: fileFormat = "// file: {0}"; break;
                }

                sb.Append(String.Format(fileFormat, path) + Environment.NewLine + Environment.NewLine);
            }

            sb.Append(body);

            if (debug) sb.Append(Environment.NewLine + Environment.NewLine);
        }     
    }
}
